Skip to content

[Feat] RAG 파이프라인에 Reranker 로직 구현#200

Merged
jeong1112 merged 8 commits intodevelopfrom
feat/#199-reranker
Mar 24, 2026
Merged

[Feat] RAG 파이프라인에 Reranker 로직 구현#200
jeong1112 merged 8 commits intodevelopfrom
feat/#199-reranker

Conversation

@jeong1112
Copy link
Copy Markdown
Collaborator

@jeong1112 jeong1112 commented Mar 23, 2026

Related issue 🛠

Work Description 📝

  • RAG 파이프라인에 Reranker를 구현했습니다.
  • RAG는 검색 단계에서 가져온 문서들을 그대로 LLM에 전달하는 구조이기 때문에, 문서의 순서가 곧 모델의 판단 기준이 됩니다. 특히 AI 모델은 입력 앞부분의 정보를 더 중요하게 활용하는 경향이 있기 때문에, 가장 중요한 문서가 중간이나 뒤쪽에 위치할 경우 해당 정보를 충분히 반영하지 못하는 문제가 발생합니다. 따라서 문서를 중요도에 따라 재정렬하는 Reranker 로직을 도입했습니다.
  • Cohere에서 제공하는 전문 Reranker API를 호출하는 형태로 구현했습니다.
  • 또한 수정된 온보딩으로 인해 Member에 필드가 추가되어 이를 RAG Context에 반영했습니다.

Summary by CodeRabbit

릴리스 노트

  • New Features
    • 사용자 프로필에 이메일, 생년월일, 대학, 관심 분야, 준비 상태, 영어 수준 등 추가 정보 필드가 표시됩니다.
    • 정책 문서 검색에 AI 기반 리랭킹을 도입하여 관련도 높은 문서를 상단에 노출합니다.
    • 리랭킹 결과는 우선순위에 따라 제한된 수만 반환되어 더 정확한 검색 결과를 제공합니다.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 23, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

정책 문서 검색에 Cohere 기반 리랭킹을 도입하고, 회원 프로필 컨텍스트 빌더에 추가 필드와 포맷 헬퍼를 적용했습니다. 관련 DTO, 클라이언트, 설정 및 검색 흐름이 추가/수정되었습니다.

Changes

Cohort / File(s) Summary
Cohere 리랭킹 클라이언트 및 DTO
src/main/java/org/sopt/kareer/global/external/cohere/service/CohereRerankClient.java, src/main/.../cohere/dto/request/CohereRerankRequest.java, src/main/.../cohere/dto/response/CohereRerankResponse.java
Cohere 리랭크 HTTP 클라이언트 추가. 요청/응답 레코드(model, query, documents, top_n 등) 및 응답 파싱과 인덱스 기반 결과 재매핑 로직 구현. 예외/빈 응답 시 원본 문서 반환 폴백 포함.
Cohere 설정 추가
src/main/java/org/sopt/kareer/global/external/cohere/properties/CohereProperties.java, src/main/resources/application.yml
Cohere 관련 구성 프로퍼티( apiKey, baseUrl, rerank.model, rerank.top-n ) 및 application.yml에 common 프로파일 설정 추가.
정책 문서 검색 파이프라인 변경
src/main/java/org/sopt/kareer/global/external/ai/service/PolicyDocumentRetriever.java
검색을 두 단계로 변경: vector store에서 더 많은 후보(candidatePoolTopK) 수집 후 Cohere로 리랭크하여 최종 topK(policyTopK) 반환하도록 로직 변경 및 CohereRerankClient 의존성 주입 추가.
회원 컨텍스트 빌더 리팩토링
src/main/java/org/sopt/kareer/global/external/ai/builder/context/MemberContextBuilder.java
프로필 텍스트 빌드에서 헬퍼(appendLine, LocalDate 처리 등) 사용으로 리팩토링. email, birthDate, university, fieldsOfInterest, preparationStatus, englishLevel 등 필드 추가 및 출력 순서 변경.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as Client
    participant Retriever as PolicyDocumentRetriever
    participant VectorStore as Policy Vector Store
    participant CohereClient as Cohere Rerank Client
    participant CohereAPI as Cohere API

    Client->>Retriever: retrievePolicy(query, member)
    Retriever->>VectorStore: similaritySearch(query, topK=candidatePoolTopK)
    VectorStore-->>Retriever: candidates (larger set)
    Retriever->>CohereClient: rerank(query, candidates, policyTopK)
    CohereClient->>CohereAPI: POST /v2/rerank (query, documents, topN)
    CohereAPI-->>CohereClient: CohereRerankResponse (results with indices)
    CohereClient->>CohereClient: map results -> Document list by index
    CohereClient-->>Retriever: reranked documents
    Retriever-->>Client: final topK results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • eraser502
  • hyomee2

Poem

🐰 코드를 훑고 리랭크 춤을 추네,
후보들을 모아 순서를 다시 맞추고,
작은 인덱스가 큰 변화를 만들며,
프로필엔 새 글자가 반짝이고,
모두 함께 더 깔끔히 정렬되었네! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title '[Feat] RAG 파이프라인에 Reranker 로직 구현' directly matches the main objective of implementing reranker logic in the RAG pipeline, as confirmed by all code changes related to Cohere reranking integration.
Linked Issues check ✅ Passed The PR fully implements the coding requirements from issue #199: integrating a reranker strategy through Cohere API to reorder documents by relevance, plus extending RAG context with member fields from onboarding changes.
Out of Scope Changes check ✅ Passed The member context field extensions (email, birthDate, university, etc.) are within scope as PR description explicitly states they reflect onboarding changes required for RAG context; all other changes directly support reranker integration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#199-reranker

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/main/java/org/sopt/kareer/global/external/cohere/service/CohereRerankClient.java (1)

24-30: RestClient 인스턴스 재사용 권장

restClient() 메서드가 호출될 때마다 새로운 RestClient 인스턴스를 생성합니다. RestClient는 스레드 안전하므로 빈 초기화 시 한 번만 생성하여 재사용하는 것이 효율적입니다. 또한 커넥션/읽기 타임아웃 설정 추가도 권장합니다.

♻️ RestClient 싱글톤 및 타임아웃 적용
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import jakarta.annotation.PostConstruct;
+import java.time.Duration;
+
 `@Slf4j`
 `@Component`
 `@RequiredArgsConstructor`
 public class CohereRerankClient {

     private final CohereProperties cohereProperties;
+    private RestClient restClient;

-    private RestClient restClient() {
-        return RestClient.builder()
+    `@PostConstruct`
+    private void init() {
+        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+        factory.setConnectTimeout(Duration.ofSeconds(5));
+        factory.setReadTimeout(Duration.ofSeconds(10));
+        
+        this.restClient = RestClient.builder()
                 .baseUrl(cohereProperties.baseUrl())
                 .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + cohereProperties.apiKey())
                 .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+                .requestFactory(factory)
                 .build();
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/org/sopt/kareer/global/external/cohere/service/CohereRerankClient.java`
around lines 24 - 30, The restClient() method currently builds a new RestClient
on every call; modify CohereRerankClient to create a single reusable RestClient
instance (e.g., a private final field initialized once in the constructor or a
`@Bean`) instead of recreating it per call, and configure connection/read timeouts
on that RestClient; ensure the construction uses cohereProperties.baseUrl() and
cohereProperties.apiKey() (same values currently used) and replace usages of
restClient() to use the stored instance.
src/main/resources/application.yml (1)

111-116: Cohere API 호출에 대한 타임아웃 설정 고려

Cohere API 호출 시 타임아웃이 설정되어 있지 않습니다. 외부 API 호출이 지연될 경우 요청 스레드가 장시간 블로킹될 수 있습니다. read-timeout-msconnect-timeout-ms 설정 추가를 권장합니다.

♻️ 타임아웃 설정 추가 예시
 cohere:
   api-key: ${COHERE_API_KEY}
   base-url: https://api.cohere.com
+  connect-timeout-ms: 5000
+  read-timeout-ms: 10000
   rerank:
     model: rerank-multilingual-v3.0
     top-n: 10
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/application.yml` around lines 111 - 116, Add explicit HTTP
timeouts to the Cohere configuration by introducing properties for connect and
read timeouts (e.g., cohere.connect-timeout-ms and cohere.read-timeout-ms)
alongside the existing base-url and api-key entries; update the config block
that contains keys like cohere.api-key, cohere.base-url, and cohere.rerank to
include these timeout values with sensible defaults (for example 5000ms connect,
30000ms read) so the client code that reads cohere.* settings can use them to
avoid blocking on slow external calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/java/org/sopt/kareer/global/external/ai/builder/context/MemberContextBuilder.java`:
- Line 48: The MemberContextBuilder currently appends the "preparationStatus"
key twice via appendLine(sb, "preparationStatus",
member.getPreparationStatus()); remove the duplicate call so "preparationStatus"
is only appended once (keep the single correct appendLine usage), and ensure any
surrounding logic in the build method still preserves the intended order of
context entries.

In
`@src/main/java/org/sopt/kareer/global/external/ai/service/PolicyDocumentRetriever.java`:
- Around line 27-34: The fallback behavior returns the full candidates list
(size candidatePoolTopK) when CohereRerankClient.rerank(...) fails, but callers
expect props.policyTopK() items; update the catch/fallback path so that when
rerank throws or returns null you trim or select the top props.policyTopK()
entries from the existing candidates list (from
policyDocumentVectorStore.similaritySearch) before returning, referencing the
variables/methods CohereRerankClient.rerank, candidates, and props.policyTopK()
to locate and implement the change.

In
`@src/main/java/org/sopt/kareer/global/external/cohere/service/CohereRerankClient.java`:
- Around line 58-61: The current fallback in CohereRerankClient returns the
original documents when response is empty which can mismatch the expected topN
size; update the fallback to return a list whose size matches the requested topN
(or candidatePoolTopK if topN not passed) by trimming documents if longer or
padding/duplicating with the best-available items if shorter, and ensure this
logic is used in the method that processes response and returns 'documents'
(refer to variables/methods: response, documents, CohereRerankClient,
PolicyDocumentRetriever, candidatePoolTopK, topN) so callers always receive a
consistently-sized result.
- Around line 63-67: The loop in CohereRerankClient that builds 'reranked' uses
result.index() from response.results() without bounds checks, which can throw
IndexOutOfBoundsException if the Cohere response is invalid; update the code in
CohereRerankClient (where response.results() is iterated and
documents.get(result.index()) is called) to validate that result.index() is
within 0..documents.size()-1 before accessing documents, and on invalid indexes
either skip the entry and log a warning including the offending index and the
documents size or throw a controlled custom exception so the caller can handle
it.

---

Nitpick comments:
In
`@src/main/java/org/sopt/kareer/global/external/cohere/service/CohereRerankClient.java`:
- Around line 24-30: The restClient() method currently builds a new RestClient
on every call; modify CohereRerankClient to create a single reusable RestClient
instance (e.g., a private final field initialized once in the constructor or a
`@Bean`) instead of recreating it per call, and configure connection/read timeouts
on that RestClient; ensure the construction uses cohereProperties.baseUrl() and
cohereProperties.apiKey() (same values currently used) and replace usages of
restClient() to use the stored instance.

In `@src/main/resources/application.yml`:
- Around line 111-116: Add explicit HTTP timeouts to the Cohere configuration by
introducing properties for connect and read timeouts (e.g.,
cohere.connect-timeout-ms and cohere.read-timeout-ms) alongside the existing
base-url and api-key entries; update the config block that contains keys like
cohere.api-key, cohere.base-url, and cohere.rerank to include these timeout
values with sensible defaults (for example 5000ms connect, 30000ms read) so the
client code that reads cohere.* settings can use them to avoid blocking on slow
external calls.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: bccdff63-ac89-4f46-96e7-9ac56934de0a

📥 Commits

Reviewing files that changed from the base of the PR and between d911ef2 and ef3272b.

📒 Files selected for processing (7)
  • src/main/java/org/sopt/kareer/global/external/ai/builder/context/MemberContextBuilder.java
  • src/main/java/org/sopt/kareer/global/external/ai/service/PolicyDocumentRetriever.java
  • src/main/java/org/sopt/kareer/global/external/cohere/dto/request/CohereRerankRequest.java
  • src/main/java/org/sopt/kareer/global/external/cohere/dto/response/CohereRerankResponse.java
  • src/main/java/org/sopt/kareer/global/external/cohere/properties/CohereProperties.java
  • src/main/java/org/sopt/kareer/global/external/cohere/service/CohereRerankClient.java
  • src/main/resources/application.yml

Comment on lines +58 to +61
if (response == null || response.results() == null || response.results().isEmpty()) {
log.warn("Cohere rerank response empty. query={}", query);
return documents;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

응답이 비어있을 때의 fallback 동작 확인 필요

응답이 null이거나 비어있을 때 원본 documents(candidatePoolTopK 크기)를 그대로 반환합니다. 이 경우에도 호출부에서 기대하는 topN 크기와 불일치가 발생합니다. PolicyDocumentRetriever에서 언급한 것처럼 일관된 크기 처리가 필요합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/org/sopt/kareer/global/external/cohere/service/CohereRerankClient.java`
around lines 58 - 61, The current fallback in CohereRerankClient returns the
original documents when response is empty which can mismatch the expected topN
size; update the fallback to return a list whose size matches the requested topN
(or candidatePoolTopK if topN not passed) by trimming documents if longer or
padding/duplicating with the best-available items if shorter, and ensure this
logic is used in the method that processes response and returns 'documents'
(refer to variables/methods: response, documents, CohereRerankClient,
PolicyDocumentRetriever, candidatePoolTopK, topN) so callers always receive a
consistently-sized result.

Comment on lines +63 to +67
List<Document> reranked = new ArrayList<>();
for (CohereRerankResponse.Result result : response.results()) {
reranked.add(documents.get(result.index()));
}
return reranked;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Cohere 응답의 index 값 유효성 검증 필요

Cohere API 응답의 result.index()가 입력 documents 리스트의 범위를 벗어날 경우 IndexOutOfBoundsException이 발생할 수 있습니다. 외부 API 응답이므로 방어적 검증을 추가하는 것이 안전합니다.

🛡️ 인덱스 유효성 검증 추가
             List<Document> reranked = new ArrayList<>();
             for (CohereRerankResponse.Result result : response.results()) {
+                if (result.index() < 0 || result.index() >= documents.size()) {
+                    log.warn("Cohere returned invalid index: {}. Skipping.", result.index());
+                    continue;
+                }
                 reranked.add(documents.get(result.index()));
             }
             return reranked;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/org/sopt/kareer/global/external/cohere/service/CohereRerankClient.java`
around lines 63 - 67, The loop in CohereRerankClient that builds 'reranked' uses
result.index() from response.results() without bounds checks, which can throw
IndexOutOfBoundsException if the Cohere response is invalid; update the code in
CohereRerankClient (where response.results() is iterated and
documents.get(result.index()) is called) to validate that result.index() is
within 0..documents.size()-1 before accessing documents, and on invalid indexes
either skip the entry and log a warning including the offending index and the
documents size or throw a controlled custom exception so the caller can handle
it.

Copy link
Copy Markdown
Collaborator

@hyomee2 hyomee2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다~~

@jeong1112 jeong1112 merged commit 7d7fce3 into develop Mar 24, 2026
1 of 2 checks passed
@jeong1112 jeong1112 deleted the feat/#199-reranker branch March 24, 2026 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] RAG 파이프라인 reranker 전략 구현

2 participants